﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Vortex.Audio {

	/// <summary>
	/// Sound object represents playing sound thread. One sound source can create multiple sound sources.
	/// </summary>
	public class Sound : IDisposable {
		///<summary>Internal reference to irrKlang sound interface</summary>
		protected IrrKlang.ISound _sound;
		private SoundStopCallbackReceiver _stopCallbackReceiver;

		/// <summary>
		/// Initializes a new instance of the <see cref="Sound"/> class.
		/// </summary>
		/// <param name="sound">The irrKlang sound class.</param>
		internal Sound(IrrKlang.ISound sound) {
			_sound = sound;
			_stopCallbackReceiver = new SoundStopCallbackReceiver(this);
		}

		#region IDisposable Members

		/// <summary>
		/// Frees sound control interface (optional, can be freed automatically on finalizing object)
		/// </summary>
		public void Dispose() {
			_sound.Dispose();
		}

		#endregion

		#region Stop Callback Receiver

		///<summary>Proxies sound stop callback from irrKlang library</summary>
		class SoundStopCallbackReceiver : IrrKlang.ISoundStopEventReceiver {
			Sound _owner;

			public SoundStopCallbackReceiver(Sound owner) {
				_owner = owner;
				_owner._sound.setSoundStopEventReceiver(this);
			}

			#region ISoundStopEventReceiver Members

			/// <summary>
			/// Called when sound is stopped.
			/// </summary>
			/// <param name="sound">The sound which is stopped.</param>
			/// <param name="reason">The reason of stop.</param>
			/// <param name="userData">The user data (not needed).</param>
			public void OnSoundStopped(IrrKlang.ISound sound, IrrKlang.StopEventCause reason, object userData) {
				_owner.ProcessSoundStop((SoundStopReason)reason);
			}

			#endregion
		}

		/// <summary>
		/// Processes the sound stop callback.
		/// </summary>
		/// <param name="reason">The reason of sound stop.</param>
		protected void ProcessSoundStop(SoundStopReason reason) {
			SoundStopEventInfo eventInfo = new SoundStopEventInfo(this, reason);

			if (null != OnStop) OnStop(eventInfo);
			if (null != OnFinish && reason == SoundStopReason.FinishedPlaying) OnFinish(eventInfo);
			if (null != OnUserStop && reason == SoundStopReason.StoppedByUser) OnUserStop(eventInfo);
			if (null != OnRemoveSource && reason == SoundStopReason.StoppedBySourceRemoval) OnRemoveSource(eventInfo);
		}

		#endregion

		/// <summary>
		/// Gets or sets the volume of sound.
		/// </summary>
		/// <value>The volume of sound (value in range 0..1).</value>
		public float Volume {
			get { return _sound.Volume; }
			set { _sound.Volume = value; }
		}

		/// <summary>
		/// Gets or sets the sound pan. Range -1 (far left) .. 1 (far right).
		/// </summary>
		/// <value>The pan. Range -1 (far left) .. 1 (far right).</value>
		public float Pan {
			get { return _sound.Pan; }
			set { _sound.Pan = value; }
		}

		/// <summary>
		/// Gets or sets a value indicating whether this sound is looped.
		/// </summary>
		/// <value><c>true</c> if this sound is looped; otherwise, <c>false</c>.</value>
		public bool IsLooped {
			get { return _sound.Looped; }
			set { _sound.Looped = value; }
		}

		/// <summary>
		/// Gets or sets a value indicating whether this sound is paused.
		/// </summary>
		/// <value><c>true</c> if this sound is paused; otherwise, <c>false</c>.</value>
		public bool IsPaused {
			get { return _sound.Paused; }
			set { _sound.Paused = value; }
		}

		/// <summary>
		/// Gets a value indicating whether this sound is finished (means sound reached its end). Finished sound can't be resumed.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this sound is finished; otherwise, <c>false</c>.
		/// </value>
		public bool IsFinished {
			get { return _sound.Finished; }
		}

		/// <summary>
		/// Gets or sets the speed of sound. Normal (original) speed is 1.0f
		/// </summary>
		/// <value>The speed of sound. Normal (original) speed is 1.0f</value>
		public float Speed {
			get { return _sound.PlaybackSpeed; }
			set { _sound.PlaybackSpeed = value; }
		}

		/// <summary>
		/// Gets or sets the playback position in milliseconds.
		/// </summary>
		/// <value>The playback position in milliseconds.</value>
		public uint PlaybackPosition {
			get { return _sound.PlayPosition; }
			set { _sound.PlayPosition = value; }
		}

		/// <summary>
		/// Gets the length of sound in milliseconds.
		/// </summary>
		/// <value>The length in milliseconds.</value>
		public uint Length {
			get { return _sound.PlayLength; }
		}

		/// <summary>
		/// Stops this sound (can't be resumed) forever.
		/// </summary>
		public void Stop() {
			_sound.Stop();
		}

		/// <summary>
		/// Pauses this sound until Resume() will be called.
		/// </summary>
		public void Pause() {
			IsPaused = true;
		}

		/// <summary>
		/// Resumes this sound if it is paused.
		/// </summary>
		public void Resume() {
			IsPaused = false;
		}

		/// <summary>
		/// Occurs when sound is stopped because of some reasons.
		/// </summary>
		public event SoundStopHandler OnStop;

		/// <summary>
		/// Occurs when sound is finished because playback reached end of sound data.
		/// </summary>
		public event SoundStopHandler OnFinish;

		/// <summary>
		/// Occurs when user stopped sound for a some reasons.
		/// </summary>
		public event SoundStopHandler OnUserStop;

		/// <summary>
		/// Occurs when sound is stopped because of source removal.
		/// </summary>
		public event SoundStopHandler OnRemoveSource;

	}
}
